package com.tengju.bff.interfaces.shared.servlet;

import com.tengju.bff.interfaces.shared.ApiResponse;
import com.tengju.user.application.shared.ApplicationException;
import com.tengju.user.domain.model.login.JwtToken;
import com.tengju.user.domain.model.login.LoginResult;
import com.tengju.user.domain.model.login.SsoToken;
import com.tengju.user.domain.model.login.UserType;
import com.tengju.user.domain.shared.JsonUtil;
import com.tengju.user.domain.shared.ResultCodeEnum;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
import org.apache.shiro.web.util.WebUtils;
import org.springframework.http.HttpStatus;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

@Slf4j
@AllArgsConstructor
public class JwtFilter extends BasicHttpAuthenticationFilter {


    private Boolean needLogHeader;

    private static final String MIME_TYPE_JSON = "application/json";
    private static final String MIME_TYPE_CHARACTER_ENCODING = "UTF-8";


    private static final String STATUS_CHECK_URL = "/tengju/status.ok";

    @Override
    protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
        //跨域处理
        HttpServletRequest httpRequest = WebUtils.toHttp(request);
        HttpServletResponse httpResponse = WebUtils.toHttp(response);
        httpResponse.setHeader("Access-Control-Allow-Origin", httpRequest.getHeader("Origin"));
        httpResponse.setHeader("Access-Control-Allow-Methods", httpRequest.getMethod());
        httpResponse.setHeader("Access-Control-Allow-Headers", httpRequest.getHeader("Access-Control-Request-Headers"));
        if (httpRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
            httpResponse.setStatus(HttpStatus.OK.value());
            return false;
        }

        return super.preHandle(request, response);
    }

    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
        if ("OPTIONS".equals(httpServletRequest.getMethod())) {
            httpServletResponse.setStatus(org.apache.http.HttpStatus.SC_NO_CONTENT);
            return true;
        }
        HttpServletRequest req = (HttpServletRequest) request;
        UserServletContext userServletContext = new UserServletContext();
        userServletContext.setIp(request.getRemoteAddr());
        userServletContext.setVersion(req.getHeader("tj_version"));
        userServletContext.setFrom(!StringUtils.isEmpty(req.getHeader("tj_from")) ? req.getHeader("tj_from") : req.getHeader("from"));
        userServletContext.setPlatform(req.getHeader("tj_platform"));
        userServletContext.setDevice(req.getHeader("tj_device"));
        userServletContext.setWeexVersion(req.getHeader("tj_weex_version"));
        userServletContext.setRequest((HttpServletRequest) request);
        userServletContext.setResponse((HttpServletResponse) response);
        //获取请求url
        String reqPath = WebUtils.getPathWithinApplication(WebUtils.toHttp(request));
        userServletContext.setRequestPath(reqPath);
        //技术人员请求test接口 需传密钥
        Map<String, Object> param = new HashMap<>();
        param.put("test_secret_key",Optional.ofNullable(req.getHeader("test")).orElse(""));
        userServletContext.setParamMap(param);

        ServletContextUtil.setServletContext(userServletContext);

        //校验token
        AuthenticationToken token = this.createToken(request, response);

        try {
            //认证校验
            this.getSubject(request, response).login(token);

            LoginResult loginResult = (LoginResult) SecurityUtils.getSubject().getPrincipal();


            if (loginResult.getUserType() == UserType.USER) {
                //封装servlet
                userServletContext.setUserId(loginResult.getUserId());
                userServletContext.setUserType(UserType.USER);
                userServletContext.setUserIdCode(loginResult.getUserIdCode());
            } else if (loginResult.getUserType() == UserType.STAFF) {
                userServletContext.setStaffId(loginResult.getUserId());
                userServletContext.setRealName(loginResult.getNickName());
                userServletContext.setClubIds(loginResult.getClubIdList());
                userServletContext.setGenSecIdCodes(loginResult.getGenSecIdCodeList());
                userServletContext.setRoleCodes(loginResult.getRoleCodes());
                userServletContext.setUserType(UserType.STAFF);
            }
            userServletContext.setRequest((HttpServletRequest) request);
            userServletContext.setResponse((HttpServletResponse) response);
            ServletContextUtil.setServletContext(userServletContext);

            printLog(httpServletRequest, userServletContext, userServletContext.getRequestPath());

            return true;
        } catch (ApplicationException e) {
            log.warn("path:{},token overdue : ",WebUtils.getPathWithinApplication(WebUtils.toHttp(request)), e);
        } catch (Exception e) {
            log.warn("path:{},token check exception : ",WebUtils.getPathWithinApplication(WebUtils.toHttp(request)),  e);
        }

        return false;
    }

    private void printLog(HttpServletRequest req, UserServletContext userServletContext, String path) {

        if (needLogHeader) {
            if (!STATUS_CHECK_URL.equals(path)) {
                log.debug("\r\n-->requestUrl:{}\r\n-->servletContext:{}\r\n-->token:{}",
                        path,
                        userServletContext.toString(),
                        Optional.ofNullable(req.getHeader("tj_token")).orElse(req.getHeader("token")));
            }
        }


    }

    @Override
    public boolean onAccessDenied(ServletRequest request, ServletResponse response) {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
        if ("OPTIONS".equals(httpServletRequest.getMethod())) {
            httpServletResponse.setStatus(org.apache.http.HttpStatus.SC_NO_CONTENT);
            return true;
        }
        responseJsonResult(response, ApiResponse.newFailure(ResultCodeEnum.TOKEN_ILLEGAL));
        return false;
    }


    @Override
    protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) {
        UserServletContext servletContext = ServletContextUtil.getServletContext();
        String path = servletContext.getRequestPath();


        HttpServletRequest req = (HttpServletRequest) request;
        String token = Optional.ofNullable(req.getHeader("tj_token"))
                .orElse(req.getHeader("token"));

        if (path.startsWith("/boss/") || path.startsWith("/export/")) {
            return new SsoToken(token);
        } else {
            return new JwtToken(token);
        }


    }




    /**
     * 输出JSON结果
     *
     * @param response
     * @param apiResponse
     * @return
     */
    private void responseJsonResult(ServletResponse response, ApiResponse apiResponse) {
        response.setContentType(MIME_TYPE_JSON);
        response.setCharacterEncoding(MIME_TYPE_CHARACTER_ENCODING);
        try (PrintWriter out = response.getWriter()) {
            out.append(JsonUtil.toJSONString(apiResponse));
        } catch (Exception e) {
            log.error("输出结果错误", e);
        }
    }


}
